"""
装饰器 介绍
"""

# 1. 函数作为参数形式调用的传统方式


def book(name):
    return 'the name of my book is {0}'.format(name)


# p_deco 作为装饰器函数
def p_deco(func):
    def wrapper(name):
        return '<p>{0}</p>'.format(func(name))
    return wrapper


deco = p_deco(book)
s = deco('java')
print(s)


# 2. 使用修饰器方式
# 使用 @ + 修饰函数，来装饰需要被装饰的函数。Python 内置的 3 种函数装饰器，分别是 @staticmethod、@classmethod 和 @property，
# 其中 staticmethod()、classmethod() 和 property() 都是 Python 的内置函数

@p_deco
def book_1(name):
    return 'the name of my book is {0}'.format(name)


book__ = book_1('java')
print(book__)

# 3. 使用多层修饰器


def div_deco(func):
    def wrapper(name):
        return '<div>{0}</div>'.format(func(name))
    return wrapper


@div_deco
@p_deco
def book_2(name):
    return 'the name of my book is {0}'.format(name)


print(book_2('java'))
# 调换一下装饰顺序，可以看到打印结果是不同的


@p_deco
@div_deco
def book_3(name):
    return 'the name of my book is {0}'.format(name)


print(book_3('java'))


# 4. 小练习：获取函数执行的时长
import time


def get_duration(func):
    # 开始时间
    time_time = time.time()
    # 执行函数
    func()
    # 结束时间
    f = time.time()
    print('执行时长为：' + str(f - time_time))
    return func


size = 5000000


@get_duration
def get_list_append():
    l = []
    for i in range(size):
        l.append(i)


@get_duration
def get_list():
    [i for i in range(size)]


# 通过对比可以看到，通过列表解析的方式初始化列表，比通过循环插入方式快
print(get_list_append())
print(get_list())


# 细心可能会发现一个问题，即当被修饰函数无参数时，可以直接将 本修饰函数 作为 修饰器函数 的参数传入。但是，如果被修饰的函数本身带有参数，那应该如何传
# 值呢？比较简单的解决方法就是在函数装饰器中嵌套一个函数，该函数带有的参数个数和被装饰器修饰的函数相同，例如 2. 和 3. 部分。下面通过嵌套函数来获取函
# 数执行的时长

def get_time(func):
    # 参数 和 被修饰函数个数相同
    def wrapper(x, y):
        time_time = time.time()
        r = func(x, y)
        f = time.time()
        # 返回执行时长 和 函数自身执行结果
        return f - time_time, r
    return wrapper


@get_time
def get_list_1(x, y):
    l = []
    for i in range(x, y):
        l.append(i)
    return l


@get_time
def get_list_2(x, y):
    return [i for i in range(x, y)]


t1, f1 = get_list_1(1, size)
print(t1)
print(len(f1))
t2, f2 = get_list_2(1, size)
print(t2)
print(len(f2))

# 还可以思考一下，如果修饰器中的内置函数使用的是万能参数形式：(*args,**kwargs) ，是不是就可以将修饰函数的适用性大大提升


def get_time_2(func):
    # 参数 和 被修饰函数个数相同
    def wrapper_2(*args, **kwargs):
        time_time = time.time()
        r = func(*args, **kwargs)
        f = time.time()
        # 返回执行时长 和 函数自身执行结果
        return f - time_time, r
    return wrapper_2


size2 = 100


@get_time_2
def get_list_append_22():
    l = []
    for i in range(size2):
        l.append(i)


@get_time_2
def get_list_22():
    [i for i in range(size2)]


@get_time_2
def get_list_12(x, y):
    l = []
    for i in range(x, y):
        l.append(i)
    return l


@get_time_2
def get_list_32(x, y):
    return [i for i in range(x, y)]


print(get_list_append_22())
print(get_list_22())
print(get_list_12(1, size2))
print(get_list_32(1, size2))
